#include "parser.h"
#include <string.h>
#include <stdlib.h>

int token;

//Da inicio a leitura dos tokens lendo o primeiro deles.
//Solucao encontrada pois o metodo  pode ser chamado mais vezes.
void iniciaParser() {
    token = proximoToken();
}

//Imprime o token encontrado quando ele eh esperado e pega o proximo.
void match(int tk, char* lex = ""){
    fprintf(stdout, "MATCH: %s%s%s\n", nomeToken(tk), (lex == "" ? "" : "."), lex);
    token = proximoToken();
}

//Itera pelos conjunto SYNC ate encontrar um token valido para que
//seja possivel dar continuidade na Analise Sintatica de forma
//a nao parar por causa de um erro na escrita do programa.
void eat(int sync[]){
    while(token != TOKEN_EOF) {
        int c = 0;
        int eatToken = sync[c];
        while(true) {
            if(eatToken == TOKEN_EOF) break;
            if(token == eatToken) {
                return;
            }
            eatToken = sync[++c];
        }
        token = proximoToken();
    }
}

//Tenta fazer o match e caso nao seja possivel, imprime a mensagem de erro
//e tenta fazer a recuperacao atraves do conjunto SYNC.
void eatOrSkipTo(int tk, int sync[], char* lex = ""){
    if(tk == token) match(tk, lex);
    else {
        erroSintatico(ERRO_TOKEN_INVALIDO, token, getLinha());
        eat(sync);
    }
}

//Program que serah retornado.
Program* _program = NULL;

//Solucao encontrada para o problema do FunctionList estar
//conflitando com VarList e TypeList na escrita do programa.
void adicionaFuncao(FunctionList* fl) {
    if(_program->functionList == NULL) {
        _program->functionList = fl;
        return;
    }
    FunctionList* functionListFilho = _program->functionList;
    while(true) {
        if(functionListFilho->functionList == NULL) {
            functionListFilho->functionList = fl;
            return;
        }
        functionListFilho = functionListFilho->functionList;
    }
}

//Solucao encontrada para o problema do VarList estar
//conflitando com FunctionList e TypeList na escrita do programa.
void adicionaVarList(VarList* vl) {
    if(_program->varList == NULL) {
        _program->varList = vl;
        return;
    }
    VarList* varListFilho = _program->varList;
    while(true) {
        if(varListFilho->varList == NULL) {
            varListFilho->varList = vl;
            return;
        }
        varListFilho = varListFilho->varList;
    }
}

//Solucao encontrada para o problema do TypeList estar
//conflitando com VarList e FunctionList na escrita do programa.
void adicionaTypeList(TypeList* tl) {
    if(_program->typeList == NULL) {
        _program->typeList = tl;
        return;
    }
    TypeList* typeListFilho = _program->typeList;
    while(true) {
        if(typeListFilho->typeList == NULL) {
            typeListFilho->typeList = tl;
            return;
        }
        typeListFilho = typeListFilho->typeList;
    }
}

int programSync[] = {TOKEN_EOF};
Program* program() {
    //Se nao houver Program* instanciado, cria um.
    if(_program == NULL) {
        _program = new Program(NULL, NULL, NULL, getLinha());
        if (token == TOKEN_EOF) {
            erroSintatico(ERRO_ARQUIVO_VAZIO, NULL,NULL);
            return _program;
        }
    }

    fprintf(stdout, "Program\n");
    if(token != TOKEN_EOF) {
        if(token == TYPEDEF) {
            typeDecl();
            program();
        } else if(token == INT || token == FLOAT || token == BOOLEAN || token == STRUCT){
            Type* _type = type();
            Id *id = new Id(getLexema(), getLinha());
            eatOrSkipTo(TOKEN_ID, programSync, id->id);
            decl(_type, id);
        } else {
            erroSintatico(ERRO_TOKEN_INVALIDO, token, getLinha());
        }
    }
    return _program;
}

int typeDeclSync[] = { TYPEDEF, STRUCT,TOKEN_LBRACE, INT,FLOAT,BOOLEAN,TOKEN_RBRACE,TOKEN_SEMICOLON,TOKEN_EOF };
void typeDecl() {
    fprintf(stdout, "TypeDecl\n");
    if(token == TYPEDEF) {
        match(TYPEDEF);
        eatOrSkipTo(STRUCT, typeDeclSync);
        eatOrSkipTo(TOKEN_LBRACE, typeDeclSync);
        VarList* varList = new VarList(NULL, NULL, getLinha());
        varList = varDecl(varList);
        eatOrSkipTo(TOKEN_RBRACE, typeDeclSync);
        Id* id = new Id(getLexema(), getLinha());
        eatOrSkipTo(TOKEN_ID, typeDeclSync, id->id);
        eatOrSkipTo(TOKEN_SEMICOLON, typeDeclSync);

        //Adiciona o struct a lista de structs do programa.
        adicionaTypeList(new TypeList(varList, id, NULL, getLinha()));

        if (token == TOKEN_EOF){
            erroSintatico(ERRO_FIM_ARQUIVO_TYPELIST, token, getLinha());
            return;
        }
        typeDecl();
    }
    return;
}

int varDeclSync[] = { TOKEN_RBRACE,INT, FLOAT, BOOLEAN, IF, WHILE, SWITCH, BREAK, PRINTLN, READLN, TOKEN_UNARYNEGATION, TOKEN_PLUS, TOKEN_MINUS, TOKEN_NUM,
                    TOKEN_LITERAL, TOKEN_LPAREN, TRUE, FALSE, TOKEN_SEMICOLON, RETURN, TOKEN_LBRACE, TOKEN_EOF };
VarList* varDecl(VarList* _varList) {
    fprintf(stdout, "VarDecl\n");
    if(token == STRUCT || token == INT || token == FLOAT || token == BOOLEAN){
        Type* _type = type();
        VarList* vl = _varList;
        while(true) {
            if(vl->varList == NULL) {
                vl->varList = idList(_type);
                break;
            }
            vl = vl->varList;
        }
        eatOrSkipTo(TOKEN_SEMICOLON, varDeclSync);
        return varDecl(_varList);
    }
    return _varList;
}

int varDecl1Sync[] = {TOKEN_SEMICOLON, TOKEN_EOF};
void varDecl1(Type* t, Id* id) {
    fprintf(stdout, "VarDecl'\n");
    NameDecl* nameDecl = new NameDecl(t, id, getLinha());
    //Adiciona uma declaracao de variavel a lista de variaveis globais.
    adicionaVarList(idList1(nameDecl));
    eatOrSkipTo(TOKEN_SEMICOLON, varDecl1Sync);
    varDecl2();
    return;
}


int varDecl2Sync[] = { TOKEN_SEMICOLON, TOKEN_EOF };
void varDecl2() {
    fprintf(stdout, "VarDecl''\n");
    if(token == STRUCT || token == INT || token == FLOAT || token == BOOLEAN) {
        Type* t = type();
        Id *id = new Id(getLexema(), getLinha());
        eatOrSkipTo(TOKEN_ID, varDecl2Sync, id->id);
        decl(t, id);
        return;
    } else if(token == TYPEDEF) {
        typeDecl();
        program();
        return;
    }
    erroSintatico(ERRO_FIM_ARQUIVO_VARLIST, token, getLinha());
    return;
}

Type* cloneType(Type* _type) {
    if(_type->tipoSubClasse() == 1) {
        PrimitiveType *cast  = (PrimitiveType*)_type;

        char* cloneSize = (char*)malloc(sizeof(cast->size));
        strcpy(cloneSize, cast->size);

        PrimitiveType *clone = new PrimitiveType(cast->primitive, cloneSize, getLinha());
        return clone;
    }
    if(_type->tipoSubClasse() == 2) {
        IdType *cast  = (IdType*)_type;

        char* cloneSize = (char*)malloc(sizeof(cast->size));
        strcpy(cloneSize, cast->size);
        char* cloneId = (char*)malloc(sizeof(cast->id->id));
        strcpy(cloneId, cast->id->id);

        IdType *clone = new IdType(new Id(cloneId, getLinha()), cloneSize, getLinha());
        return clone;
    }
    return _type;
}

int idListSync[] = { TOKEN_ID, TOKEN_SEMICOLON, TOKEN_EOF };
VarList* idList(Type* type) {
    fprintf(stdout, "IdList\n");
    Id *id = new Id(getLexema(), getLinha());
    eatOrSkipTo(TOKEN_ID, idListSync, id->id);
    NameDecl* nameDecl = new NameDecl(type, id, getLinha());
    return idList1(nameDecl);
}

int idList1Sync[] = {TOKEN_ID,TOKEN_SEMICOLON, TOKEN_EOF };
VarList* idList1(NameDecl* nameDecl) {
    fprintf(stdout, "IdList'\n");
    if(token == TOKEN_LEFTSQUAREBRACKET || token == TOKEN_COMMA) return idList2(nameDecl);
    return new VarList(nameDecl, NULL, getLinha());
}

int idList2Sync[] = { TOKEN_SEMICOLON, TOKEN_ID, TOKEN_LBRACE, TOKEN_RBRACE, TOKEN_EOF };
VarList* idList2(NameDecl* nameDecl) {
    fprintf(stdout, "IdList''\n");
    if(token == TOKEN_LEFTSQUAREBRACKET) {
        Num* tamArray = array();
        if(tamArray != NULL) nameDecl->type->size = tamArray->num;
    }
    if(token == TOKEN_COMMA) {
        match(TOKEN_COMMA);
        Id *id = new Id(getLexema(), getLinha());
        eatOrSkipTo(TOKEN_ID, idList2Sync, id->id);
        //Neste caso o TYPE eh o mesmo pois as variaveis
        //sao separadas por virgula (COMMA).
        Type* _clone = cloneType(nameDecl->type);
        NameDecl* _nameDecl = new NameDecl(_clone, id, getLinha());
        return new VarList(nameDecl, idList1(_nameDecl), getLinha());
    }
    return new VarList(nameDecl, NULL, getLinha());
}

int declSync[] = {TOKEN_SEMICOLON, TOKEN_ID, TOKEN_COMMA, TOKEN_RPAREN, TOKEN_EOF};
void decl(Type* t, Id* id) {
    fprintf(stdout, "Decl\n");
    if(token == TOKEN_LEFTSQUAREBRACKET || token == TOKEN_COMMA || token == TOKEN_SEMICOLON) {
        varDecl1(t, id);
        return;
    } else if(token == TOKEN_LPAREN) {
        //Inicio da construcao do FunctionList. O resto sera construido
        //dentro da funcao functionDecl().
        FunctionList* fl = new FunctionList(t, id, NULL, NULL, NULL, NULL, getLinha());
        functionDecl(fl);
        decl1();
        return;
    }
    program();
}

int decl1Sync[] = { TOKEN_SEMICOLON, TOKEN_RPAREN, TOKEN_EOF };
void decl1() {
    fprintf(stdout, "Decl'\n");
    if(token != TOKEN_EOF) program();
    return;
}

int functionDeclSync[] = { TOKEN_RPAREN, TOKEN_LBRACE, TOKEN_RBRACE, TYPEDEF, INT, FLOAT, BOOLEAN, STRUCT, TOKEN_EOF };
void functionDecl(FunctionList* fl) {
    fprintf(stdout, "FunctionDecl\n");
    match(TOKEN_LPAREN);
    fl->varList1 = formalList();
    eatOrSkipTo(TOKEN_RPAREN, functionDeclSync);
    eatOrSkipTo(TOKEN_LBRACE, functionDeclSync);
    VarList* varList2 = new VarList(NULL, NULL, getLinha());
    fl->varList2 = varDecl(varList2);
    fl->statementList = stmtList();
    eatOrSkipTo(TOKEN_RBRACE, functionDeclSync);
    //Adiciona a Funcao a lista de funcoes do programa.
    adicionaFuncao(fl);
}

int arraySync[] = { TOKEN_NUM, TOKEN_RIGHTSQUAREBRACKET, TOKEN_SEMICOLON, TOKEN_COMMA, TOKEN_EOF };
Num* array() {
    fprintf(stdout, "Array\n");
    if(token == TOKEN_LEFTSQUAREBRACKET) {
        match(TOKEN_LEFTSQUAREBRACKET);
        Num *num = new Num(getLexema(), getLinha());
        eatOrSkipTo(TOKEN_NUM, arraySync, num->num);
        eatOrSkipTo(TOKEN_RIGHTSQUAREBRACKET, arraySync);
        return num;
    }
    return NULL;
}

int formalListSync[] = { TOKEN_ID, TOKEN_RPAREN, TOKEN_LBRACE, TOKEN_EOF };
VarList* formalList() {
    fprintf(stdout, "FormalList\n");
    if(token == STRUCT || token == INT || token == FLOAT || token == BOOLEAN) {
        Type* _type = type();
        Id *id = new Id(getLexema(), getLinha());
        eatOrSkipTo(TOKEN_ID, formalListSync, id->id);
        Num* tamArray = array();
        //Caso o tipo se trate de um array,
        //armazena o tamanho em 'size' de Type.
        if(tamArray != NULL) _type->size = tamArray->num;
        NameDecl* nameDecl = new NameDecl(_type, id, getLinha());
        return new VarList(nameDecl, formalRest(), getLinha());
    }
    return NULL;
}

int formalRestSync[] = { TOKEN_RPAREN, TOKEN_ID, INT, FLOAT, BOOLEAN, TOKEN_EOF };
VarList* formalRest() {
    fprintf(stdout, "FormalRest\n");
    if(token == TOKEN_COMMA) {
        match(TOKEN_COMMA);
        return formalList();
    }
    return NULL;
}

int typeSync[] = { TOKEN_ID, TOKEN_EOF };
Type* type() {
    fprintf(stdout, "Type\n");

    if(token == STRUCT) {
        match(STRUCT);
        Id *id = new Id(getLexema(), getLinha());
        eatOrSkipTo(TOKEN_ID, typeSync, id->id);
        return new IdType(id, "1", getLinha());
    } else if(token == INT || token == FLOAT || token == BOOLEAN) {
        PrimitiveType* type = new PrimitiveType(token, "1", getLinha());
        match(token);
        return type;
    }
    return NULL;
}

int stmtListSync[] = { TOKEN_RBRACE, CASE, TOKEN_EOF };
StatementList* stmtList() {
    fprintf(stdout, "StmtList\n");
    return new StatementList(stmt(), stmtList1(), getLinha());
}

int stmtList1Sync[] = { TOKEN_RBRACE, CASE, TOKEN_EOF };
StatementList* stmtList1() {
    fprintf(stdout, "StmtList'\n");
    if(firstStmt(token) == 1) return new StatementList(stmt(), stmtList1(), getLinha());
    return NULL;
}

int stmtSync[] = { TOKEN_RBRACE, IF, WHILE, SWITCH, BREAK, PRINTLN, READLN, TOKEN_UNARYNEGATION, TOKEN_PLUS, TOKEN_MINUS, TOKEN_NUM,
                    TOKEN_LITERAL, TOKEN_LPAREN,  TRUE, FALSE, TOKEN_ID, RETURN, TOKEN_LBRACE, ELSE, TOKEN_RPAREN, TOKEN_SEMICOLON, TOKEN_EOF };
Statement* stmt() {
    fprintf(stdout, "Stmt\n");

    if(token == IF) {
        match(IF);
        eatOrSkipTo(TOKEN_LPAREN, stmtSync);
        Exp* exp = expr();
        eatOrSkipTo(TOKEN_RPAREN, stmtSync);
        Statement* statement1 = stmt();
        Statement* statement2 = stmtElse();
        return new If(exp, statement1, statement2, getLinha());
    } else if(token == WHILE) {
        match(WHILE);
        eatOrSkipTo(TOKEN_LPAREN, stmtSync);
        Exp* exp = expr();
        eatOrSkipTo(TOKEN_RPAREN, stmtSync);
        Statement* statement = stmt();
        return new While(exp, statement, getLinha());
    } else if(token == SWITCH) {
        match(SWITCH);
        eatOrSkipTo(TOKEN_LPAREN, stmtSync);
        Exp* exp = expr();
        eatOrSkipTo(TOKEN_RPAREN, stmtSync);
        eatOrSkipTo(TOKEN_LBRACE, stmtSync);
        CaseBlock* _caseBlock = caseBlock();
        eatOrSkipTo(TOKEN_RBRACE, stmtSync);
        return new Switch(exp, _caseBlock, getLinha());
    } else if(token == BREAK) {
        match(BREAK);
        eatOrSkipTo(TOKEN_SEMICOLON, stmtSync);
        return new Break();
    } else if(token == PRINTLN) {
        match(PRINTLN);
        eatOrSkipTo(TOKEN_LPAREN, stmtSync);
        ExpList* expList = exprList();
        eatOrSkipTo(TOKEN_RPAREN, stmtSync);
        eatOrSkipTo(TOKEN_SEMICOLON, stmtSync);
        return new Println(expList, getLinha());
    } else if(token == READLN) {
        match(READLN);
        eatOrSkipTo(TOKEN_LPAREN, stmtSync);
        Exp* exp = expr();
        eatOrSkipTo(TOKEN_RPAREN, stmtSync);
        eatOrSkipTo(TOKEN_SEMICOLON, stmtSync);
        return new Read(exp, getLinha());
    } else if(token == RETURN) {
        match(RETURN);
        Exp* exp = expr();
        eatOrSkipTo(TOKEN_SEMICOLON, stmtSync);
        return new Return(exp, getLinha());
    } else if(token == TOKEN_LBRACE) {
        eatOrSkipTo(TOKEN_LBRACE, stmtSync);
        StatementList* statementList = stmtList();
        eatOrSkipTo(TOKEN_RBRACE, stmtSync);
        return statementList;
    } else if(firstExp(token)) {
        Exp* exp = expr();
        eatOrSkipTo(TOKEN_SEMICOLON, stmtSync);
        return exp;
    } else {
        erroSintatico(ERRO_TOKEN_INVALIDO, token, getLinha());
        eat(stmtSync);
        return NULL;
    }
}

int stmtElseSync[] = { TOKEN_RBRACE, IF, WHILE, SWITCH, BREAK, PRINTLN, READLN, TOKEN_UNARYNEGATION, TOKEN_PLUS, TOKEN_MINUS, TOKEN_NUM,
                        TOKEN_LITERAL, TOKEN_LPAREN, TRUE, FALSE, TOKEN_ID, RETURN, TOKEN_LBRACE, ELSE, TOKEN_EOF };
Statement* stmtElse() {
    fprintf(stdout, "StmtElse\n");
    if(token == ELSE) {
        match(ELSE);
        return stmt();
    }
    return NULL;
}

int exprSync[] = { TOKEN_RPAREN, RETURN, TOKEN_SEMICOLON, TOKEN_COMMA, TOKEN_RIGHTSQUAREBRACKET, TOKEN_EOF };
Exp* expr() {
    fprintf(stdout, "Expr\n");
    Exp* exp = assign();
    return exp;
}

int exprListSync[] = { TOKEN_RPAREN, TOKEN_EOF };
ExpList* exprList() {
    fprintf(stdout, "ExprList\n");
    if(firstExp(token) == 1) return exprListTail();
    return NULL;
}

int exprListTailSync[] = { TOKEN_RPAREN, TOKEN_EOF};
ExpList* exprListTail() {
    fprintf(stdout, "ExprListTail\n");
    Exp* exp = expr();
    if(token == TOKEN_COMMA) return new ExpList(exp, exprListTail1(), getLinha());
    return new ExpList(exp, NULL, getLinha());
}

int exprListTail1Sync[] = {TOKEN_RPAREN, TOKEN_EOF};
ExpList* exprListTail1() {
    fprintf(stdout, "ExprListTail'\n");
    if(token == TOKEN_COMMA) {
        match(TOKEN_COMMA);
        if(firstExp(token)) {
            Exp* exp = expr();
            if(token == TOKEN_COMMA) return new ExpList(exp, exprListTail1(), getLinha());
            return new ExpList(exp, NULL, getLinha());
        }
    }
    return NULL;
}

int caseBlockSync[] = { CASE, TOKEN_RBRACE, TOKEN_EOF };
CaseBlock* caseBlock() {
    fprintf(stdout, "CaseBlock\n");
    if(token == CASE) {
        match(CASE);
        Num *num = new Num(getLexema(), getLinha());
        eatOrSkipTo(TOKEN_NUM, caseBlockSync, num->num);
        eatOrSkipTo(TOKEN_COLON, caseBlockSync);
        if(firstStmt(token) == 1) return new CaseBlock(num, stmtList(), caseBlock(), getLinha());
        //Existe a possibilidade de CASEBLOCK's vazios.
        return new CaseBlock(num, NULL, caseBlock(), getLinha());
    }
    return NULL;
}

int assignSync[] = {  TOKEN_RPAREN, TOKEN_SEMICOLON, TOKEN_COMMA, TOKEN_RIGHTSQUAREBRACKET, TOKEN_EOF };
Exp* assign() {
    fprintf(stdout, "Assign\n");
    Exp* exp1 = _or();
    Exp* exp2 = assign1();
    if(exp2 == NULL) return exp1;
    return new Assign(exp1, exp2, getLinha());
}

int assign1Sync[] = { TOKEN_RPAREN, TOKEN_SEMICOLON, TOKEN_COMMA, TOKEN_RIGHTSQUAREBRACKET, TOKEN_EOF };
Exp* assign1() {
    fprintf(stdout, "Assign'\n");
    if(token == TOKEN_ASSIGN) {
        match(TOKEN_ASSIGN);
        Exp* exp1 = _or();
        Exp* exp2 = assign1();
        if(exp2 == NULL) return exp1;
        return new Assign(exp1, exp2, getLinha());
    }
    return NULL;
}

int orSync[] = { TOKEN_ASSIGN, TOKEN_EOF };
Exp* _or() {
    fprintf(stdout, "Or\n");
    Exp* exp1 = _and();
    Exp* exp2 = or1();
    if(exp2 == NULL) return exp1;
    return new Operation(TOKEN_OR, exp1, exp2, getLinha());
}

int or1Sync[] = { TOKEN_ASSIGN, TOKEN_EOF};
Exp* or1() {
    fprintf(stdout, "Or'\n");
    if(token == TOKEN_OR) {
        match(TOKEN_OR);
        Exp* exp1 = _and();
        Exp* exp2 = or1();
        if(exp2 == NULL) return exp1;
        return new Operation(TOKEN_OR, exp1, exp2, getLinha());
    }
    return NULL;
}

int andSync[] = {TOKEN_OR, TOKEN_EOF};
Exp* _and() {
    fprintf(stdout, "And\n");
    Exp* exp1 = igualDif();
    Exp* exp2 = and1();
    if(exp2 == NULL) return exp1;
    return new Operation(TOKEN_AND, exp1, exp2, getLinha());
}

int and1Sync[] = {TOKEN_OR, TOKEN_EOF };
Exp* and1() {
    fprintf(stdout, "And'\n");
    if(token == TOKEN_AND) {
        match(TOKEN_AND);
        Exp* exp1 = igualDif();
        Exp* exp2 = and1();
        if(exp2 == NULL) return exp1;
        return new Operation(TOKEN_AND, exp1, exp2, getLinha());
    }
    return NULL;
}

int igualDifSync[] = {  TOKEN_AND, TOKEN_EOF };
Exp* igualDif() {
    fprintf(stdout, "IgualDif\n");
    Exp* exp1 = relacional();
    int tokenOperation = token;
    Exp* exp2 = igualDif1();
    if(exp2 == NULL) return exp1;
    return new Operation(tokenOperation, exp1, exp2, getLinha());
}

int igualDif1Sync[] = {  TOKEN_AND, TOKEN_EOF };
Exp* igualDif1() {
    fprintf(stdout, "IgualDif'\n");
    if(token != TOKEN_COMPAREEQUAL && token != TOKEN_NOTEQ) return NULL;
    match(token);
    Exp* exp1 = relacional();
    int tokenOperation = token;
    Exp* exp2 = igualDif1();
    if(exp2 == NULL) return exp1;
    return new Operation(tokenOperation, exp1, exp2, getLinha());
}

int relacionalSync[] = { TOKEN_COMPAREEQUAL, TOKEN_NOTEQ, TOKEN_EOF };
Exp* relacional() {
    fprintf(stdout, "Relacional\n");

    Exp* exp1 = adicSubOrBB();
    int tokenOperation = token;
    Exp* exp2 = relacional1();
    if(exp2 == NULL) return exp1;
    return new Operation(tokenOperation, exp1, exp2, getLinha());
}

int relacional1Sync[] = { TOKEN_COMPAREEQUAL, TOKEN_NOTEQ, TOKEN_EOF };
Exp* relacional1() {
    fprintf(stdout, "Relacional'\n");
    if(token != TOKEN_LESSTHAN && token != TOKEN_LESSEQUAL && token != TOKEN_GREATEREQUAL && token != TOKEN_GREATERTHAN) return NULL;
    match(token);
    Exp* exp1 = adicSubOrBB();
    int tokenOperation = token;
    Exp* exp2 = relacional1();
    if(exp2 == NULL) return exp1;
    return new Operation(tokenOperation, exp1, exp2, getLinha());
}

int adicSubOrBBSync[] = { TOKEN_LESSTHAN, TOKEN_LESSEQUAL, TOKEN_GREATERTHAN, TOKEN_GREATEREQUAL, TOKEN_EOF };
Exp* adicSubOrBB() {
    fprintf(stdout, "AdicSubOrBB\n");
    Exp* exp1 = multDivisao();
    int tokenOperation = token;
    Exp* exp2 = adicSubOrBB1();
    if(exp2 == NULL) return exp1;
    return new Operation(tokenOperation, exp1, exp2, getLinha());
}

int adicSubOrBB1Sync[] = { TOKEN_LESSTHAN, TOKEN_LESSEQUAL, TOKEN_GREATERTHAN, TOKEN_GREATEREQUAL, TOKEN_EOF };
Exp* adicSubOrBB1() {
    fprintf(stdout, "AdicSubOrBB'\n");
    if(token != TOKEN_PLUS && token != TOKEN_MINUS && token != TOKEN_BINARYOR) return NULL;
    match(token);
    Exp* exp1 = multDivisao();
    int tokenOperation = token;
    Exp* exp2 = adicSubOrBB1();
    if(exp2 == NULL) return exp1;
    return new Operation(tokenOperation, exp1, exp2, getLinha());
}

int multDivisaoSync[] = { TOKEN_PLUS, TOKEN_MINUS, TOKEN_BINARYOR, TOKEN_EOF };
Exp* multDivisao() {
    fprintf(stdout, "MultDivisao\n");
    Exp* exp1 = unarios();
    int tokenOperation = token;
    Exp* exp2 = multDivisao1();
    if(exp2 == NULL) return exp1;
    return new Operation(tokenOperation, exp1, exp2, getLinha());
}

int multDivisao1Sync[] = { TOKEN_PLUS, TOKEN_MINUS, TOKEN_BINARYOR, TOKEN_EOF };
Exp* multDivisao1() {
    fprintf(stdout, "MultDivisao'\n");
    if(token != TOKEN_MULTIPLICATION && token != TOKEN_DIV && token != TOKEN_REST && token != TOKEN_AND) return NULL;
    match(token);
    Exp* exp1 = unarios();
    int tokenOperation = token;
    Exp* exp2 = multDivisao1();
    if(exp2 == NULL) return exp1;
    return new Operation(tokenOperation, exp1, exp2, getLinha());
}

int unariosSync[] = { TOKEN_MULTIPLICATION, TOKEN_DIV, TOKEN_REST, TOKEN_BINARYAND, TOKEN_EOF };
Exp* unarios() {
    fprintf(stdout, "Unarios\n");
    if(token == TOKEN_NUM || token == TOKEN_LITERAL || token == TOKEN_ID || token == TRUE ||
        token == FALSE || token == TOKEN_LPAREN) return primary();
    else if(token == TOKEN_UNARYNEGATION) {
        match(TOKEN_UNARYNEGATION);
        Exp* exp = primary();
        Not *_not = new Not(exp, getLinha());
        return _not;
    } else if(token == TOKEN_PLUS) {
        match(TOKEN_PLUS);
        if (token == TOKEN_MINUS || token == TOKEN_PLUS) return new Sign(expr(),token, getLinha());
        Exp* exp = primary();
        Sign *_sign = new Sign(exp, token, getLinha());
        return _sign;
    } else if(token == TOKEN_MINUS) {
        match(TOKEN_MINUS);
        if (token == TOKEN_MINUS || token == TOKEN_PLUS) return new Sign(expr(),token, getLinha());
        Exp* exp = primary();
        Sign *_sign = new Sign(exp, token, getLinha());
        return _sign;
    }
    return NULL;
}

int primarySync[] = { TOKEN_MULTIPLICATION, TOKEN_DIV, TOKEN_REST, TOKEN_BINARYAND, TOKEN_RPAREN, TOKEN_SEMICOLON, TOKEN_EOF };
Exp* primary() {
    fprintf(stdout, "Primary\n");
    if(token == TOKEN_NUM) {
        Num *num = new Num(getLexema(), getLinha());
        match(TOKEN_NUM, num->num);
        return num;
    } else if(token == TOKEN_LITERAL) {
        Literal *literal = new Literal(getLexema(), getLinha());
        match(TOKEN_LITERAL, literal->literal);
        return literal;
    } else if(token == TOKEN_LPAREN) {
        match(TOKEN_LPAREN);
        Exp* exp = expr();
        eatOrSkipTo(TOKEN_RPAREN, primarySync);
        return exp;
    } else if(token == TRUE || token == FALSE) {
        Boolean* boolean = new Boolean(token, getLinha());
        match(token);
        return boolean;
    } else if(token == TOKEN_ID) {
        Id *id = new Id(getLexema(), getLinha());
        match(TOKEN_ID, id->id);
        if(token == TOKEN_LPAREN) {
            match(TOKEN_LPAREN);
            Call* call = new Call(id, exprList(), getLinha());
            eatOrSkipTo(TOKEN_RPAREN, primarySync);
            return primary2(call);
        } else if(token == TOKEN_DOT) {
            match(TOKEN_DOT);
            return new ExpAninhada(id, primary(), getLinha());
        }
        return primary1(id);
    }
    erroSintatico(ERRO_TOKEN_INVALIDO, token, getLinha());
    return NULL;
}

int primary1Sync[] = { TOKEN_MULTIPLICATION, TOKEN_DIV, TOKEN_REST, TOKEN_BINARYAND, TOKEN_RIGHTSQUAREBRACKET, TOKEN_RPAREN, TOKEN_EOF };
Exp* primary1(Exp* exp) {
    fprintf(stdout, "Primary'\n");
    if(token == TOKEN_LEFTSQUAREBRACKET) {
        match(TOKEN_LEFTSQUAREBRACKET);
        Array* array = new Array(exp, expr(), getLinha());
        eatOrSkipTo(TOKEN_RIGHTSQUAREBRACKET, primary1Sync);
        return primary2(array);
    } else if(token == TOKEN_DOT) {
        match(TOKEN_DOT);
        ExpAninhada* expAninhada = new ExpAninhada(exp, primary(), getLinha());
        return expAninhada;
    } else if(token == TOKEN_LPAREN) {
        match(TOKEN_LPAREN);
        Id* id = (Id*) exp;
        Call* call = new Call(id, exprList(), getLinha());
        eatOrSkipTo(TOKEN_RPAREN, primarySync);
        return call;
    }
    return exp;
}

int primary2Sync[] = { TOKEN_MULTIPLICATION, TOKEN_DIV, TOKEN_REST, TOKEN_BINARYAND, TOKEN_RIGHTSQUAREBRACKET, TOKEN_EOF };
Exp* primary2(Exp* exp) {
    fprintf(stdout, "Primary''\n");
    if(token == TOKEN_LEFTSQUAREBRACKET) {
        match(TOKEN_LEFTSQUAREBRACKET);
        Array* array = new Array(exp, expr(), getLinha());
        eatOrSkipTo(TOKEN_RIGHTSQUAREBRACKET, primary1Sync);
        return primary2(array);
    } else if(token == TOKEN_DOT) {
        match(TOKEN_DOT);
        ExpAninhada* expAninhada = new ExpAninhada(exp, primary(), getLinha());
        return expAninhada;
    }
    return exp;
}

//Confirma se o token faz parte do conjunto FIRST de STMT.
int firstStmt(int tk) {
    return (tk == IF || tk == WHILE || tk == SWITCH || tk == BREAK || tk == PRINTLN || tk == READLN || tk == RETURN ||
        tk == TOKEN_LBRACE || tk == TOKEN_UNARYNEGATION || tk == TOKEN_PLUS || tk == TOKEN_MINUS ||
        tk == TOKEN_NUM || tk == TOKEN_LITERAL || tk == TOKEN_ID || tk == TOKEN_LPAREN || tk == TRUE || tk == FALSE);
}

//Confirma se o token faz parte do conjunto FIRST de EXP.
int firstExp(int tk) {
    return (token == TOKEN_UNARYNEGATION || token == TOKEN_PLUS || token == TOKEN_MINUS || token == TOKEN_NUM ||
        token == TOKEN_LITERAL || token == TOKEN_ID || token == TOKEN_LPAREN || token == TRUE || token == FALSE);
}
